import os
import random
import time

import requests
from PIL import Image as Im, Image
from selenium.webdriver import ActionChains
from selenium.webdriver.chromium import webdriver
#
# import slider_infer


from ultralytics import YOLO


class SlideVerifyCode():
    """滑动验证码破解"""

    def __init__(self, count=5, save_image=True):
        """
        :param count: 验证重试的次数，默认为5次
        :param save_image: 是否保存验证过程中的图片，默认不保存
        """
        self.count = count
        self.save_image = save_image
        # self.paddle_det = slider_infer.init("slidevcode")

        # 从模型文件构建model
        self.yolo_det = YOLO("slidevcode/slidevcode.pt")

    def slide_verification(self, driver, slide_element, distance):
        """
        :param driver: driver对象
        :type driver:webdriver.Chrome
        :param slide_element: 滑块的元组
        :type slider_ele: WebElement
        :param distance:  滑动的距离
        :type: int
        :return:
        """
        start_url = driver.title
        print("需要滑动的距离为：", distance)
        locus = self.get_slide_locus(distance)
        print("生成的滑动轨迹为:{}，轨迹的距离之和为{}".format(locus, distance))
        ActionChains(driver).click_and_hold(slide_element).perform()
        time.sleep(0.5)
        for loc in locus:
            time.sleep(0.01)
            ActionChains(driver).move_by_offset(loc, random.randint(-5, 5)).perform()
            ActionChains(driver).context_click(slide_element)
        ActionChains(driver).release(on_element=slide_element).perform()
        time.sleep(2)
        end_url = driver.title
        print(start_url + "====" + end_url)
        # if start_url == end_url:
        #     return -1
        #     print("第{}次验证失败，开启重试".format(6 - self.count))
        #     self.count -= 1
        #     self.slide_verification(driver, slide_element, distance)
        return 0

    def onload_save_img(self, url, filename="image.png"):
        """
        下载图片保存
        :param url:图片地址
        :param filename: 保存的图片名
        :return:
        """
        try:
            response = requests.get(url=url)
        except(requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError) as e:
            print("图片下载失败")
            raise e
        else:
            with open(filename, "wb") as f:
                f.write(response.content)

    def get_element_slide_distance(self, slider_ele, background_ele, correct=0):
        """
        根据传入滑块，和背景的节点，计算滑块的距离

        该方法只能计算 滑块和背景图都是一张完整图片的场景，
        如果是通过多张小图拼接起来的背景图，该方法不适用，后续会补充一个专门针对处理该场景的方法
        :param slider_ele: 滑块图片的节点
        :type slider_ele: WebElement
        :param background_ele: 背景图的节点
        :type background_ele:WebElement
        :param correct:滑块缺口截图的修正值，默认为0,调试截图是否正确的情况下才会用
        :type: int
        :return: 背景图缺口位置的X轴坐标位置（缺口图片左边界位置）
        """
        background_url = background_ele.get_attribute("src")
        background = "./images/verifycode.jpg"
        self.onload_save_img(background_url, background)

        # 获取父元素的坐标
        parent_location = background_ele.location

        # 获取子元素的坐标
        child_location = slider_ele.location

        # 计算子元素在父元素中的相对坐标
        relative_x = child_location['x'] - parent_location['x']
        relative_y = child_location['y'] - parent_location['y']

        result = self.yolo_det(background)
        # if result is None or len(result) == 0:
        # result = self.paddle_det.predict(background_01, threshold=0.8)
        # result = result['boxes']
        # 再使用tensor.numpy()进行转化
        result = result[0].boxes.data.cpu().numpy()
        distance = int(result[0][0])
        print("当前滑块的信息：{},{}".format(result, distance))
        os.remove(background)
        return distance

    def get_image_slide_distance(self, slider_ele, background_ele, background_image):
        # 获取父元素的坐标
        # parent_location = background_ele.location

        # 获取子元素的坐标
        # child_location = slider_ele.location
        #
        # # 计算子元素在父元素中的相对坐标
        # relative_x = child_location['x'] - parent_location['x']
        # relative_y = child_location['y'] - parent_location['y']
        result = self.yolo_det(background_image)
        # 再使用tensor.numpy()进行转化
        result = result[0].boxes.data.cpu().numpy()
        if len(result) == 0:
            return -1
        distance = int(result[0][0])
        print("当前滑块的信息：{},{}".format(result, distance))
        return distance

    @classmethod
    def get_slide_locus(self, distance):
        """
        根据移动坐标位置构造移动轨迹,前期移动慢，中期块，后期慢
        :param distance:移动距离
        :type:int
        :return:移动轨迹
        :rtype:list
        """
        remaining_dist = distance
        locus = []
        while remaining_dist > 0:
            ratio = remaining_dist / distance
            if ratio < 0.2:
                span = random.randint(2, 8)
            elif ratio > 0.8:
                span = random.randint(5, 8)
            else:
                span = random.randint(10, 16)
            locus.append(span)
            remaining_dist -= span
        return locus

    def image_crop(self, image, location, new_name="new_image.png"):
        """
        对图片的指定位置进行截图
        :param image: 被截取图片的坐标位置
        :param location:需要截图的坐标位置：（left,top,right,button）
        :type location: tuple
        :return:
        """
        image = Im.open(image)
        imagecrop = image.crop(location)
        imagecrop.save(new_name)


if __name__ == "__main__":
    # 1、创建一个driver对象，访问qq登录页面
    browser = webdriver.Chrome()
    browser.get("https://qzone.qq.com/")

    # 2、输入账号密码

    # 2.0 点击切换到登录的iframe
    browser.switch_to.frame('login_frame')

    # 2.1 点击账号密码登录
    browser.find_element_by_id('switcher_plogin').click()

    # 2.2定位账号输入框，输入账号
    browser.find_element_by_id("u").send_keys("1938091409")

    # 2.3定位密码输入输入密码
    browser.find_element_by_id("p").send_keys("aini2141339856.0")

    # 3、点击登录
    browser.find_element_by_id('login_button').click()
    time.sleep(3)

    # # 4、模拟滑动验证
    # # 4.1切换到滑动验证码的iframe中
    # tcaptcha = browser.find_element_by_id("tcaptcha_iframe")
    # browser.switch_to.frame(tcaptcha)

    # 4.2选择拖动滑块的节点
    slide_element = browser.find_element_by_id('/html/body/div[4]/div[2]/div/div/div[2]/div/div[2]/div[2]')

    #  模拟拖到滑块进行识别
    sc = SlideVerifyCode(save_image=True)

    #
    # 获取滑块图片的节点id="slideBlock"
    slideBlock_ele = browser.find_element_by_id('slideBlock')
    # 获取背景图片节点id="slideBg"
    slideBg = browser.find_element_by_id('slideBg')

    # 4.3计算滑动距离，电脑缩放比例需要为100% 才可确保减去的正确
    distance = sc.get_element_slide_distance(slideBlock_ele, slideBg)
    print("滑动的距离为：", distance)
    # 滑动距离误差校正，按照比例来进行计算，然后减去 第一部分距离
    distance = distance * (280 / 680) - 31

    print("校验后的滑动距离", distance)

    # 4.4、进行滑动
    sc.slide_verification(browser, slide_element, distance=distance)

    time.sleep(2)
    browser.close()
